using System;
using System.Net.WebSockets;
using System.Threading;
using foundation.events;
using foundation;

namespace foundation.socket
{

    public enum SOCKET_EVENT
    {
        OPEN,
        CLOSE,
        DISCONNECT
    }

    // readonly CancellationToken _cancellation = new CancellationToken();

    public class WebSocket : MiniDispatcher
    {
        private ClientWebSocket _webSocket;
        private CancellationToken _cancellation = new CancellationToken();
        private ArraySegment<byte> result = new ArraySegment<byte>(new byte[10000]);
        private ByteArray byteBuffer = new ByteArray();
        private Thread thread = null;
        public bool isSync = true;
        SynchronizationContext synchronizationContext;

        public bool isOpen
        {
            get
            {
                return _webSocket.State == WebSocketState.Open;
            }
        }

        private string url;

        public WebSocket()
        {
            synchronizationContext = SynchronizationContext.Current;
        }

        public async void Connect(string url)
        {
            try
            {
                _webSocket = new ClientWebSocket();
                this.url = url;
                await _webSocket.ConnectAsync(new Uri(url), _cancellation);
                Debug.LogFormat("connect {0} , state:{1}", url, _webSocket.State);
                if (!(_webSocket is { State: WebSocketState.Open })) return;
                if (null != thread)
                {
                    if (thread.ThreadState == ThreadState.Running)
                    {
                        thread.Abort();
                    }
                }
                thread = new Thread(ReceiveMessage);
                thread.Start();

                this.SimpleDispatch(SOCKET_EVENT.OPEN);
            }
            catch (Exception e)
            {
                Debug.LogFormat("connect {0} , state:{1} {2}", url, _webSocket.State, e.Message);
                if (thread?.ThreadState == ThreadState.Running)
                {
                    thread.Abort();
                }
                disconnect();
            }
            // var value = new ASObject();
            // value.Add("account", "whh");
            // // var list = new List<object>() {1, 2,"3"};
            // SimpleSend(8193, value);
        }


        private void disconnect()
        {
            Debug.LogFormat("disconnect {0} , state:{1} try to reconnect", url, _webSocket.State);
            this.SimpleDispatch(SOCKET_EVENT.DISCONNECT);
            Thread.Sleep(2000);
            //断线重连
            this.Connect(url);
        }

        public System.Threading.Tasks.Task Disconnect()
        {
            return _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "close", CancellationToken.None);
        }


        private async void ReceiveMessage()
        {
            // try
            // {
            while (_webSocket.State == WebSocketState.Open)
            {
                WebSocketReceiveResult response = null;
                try
                {
                    response = await _webSocket.ReceiveAsync(result, CancellationToken.None);
                }
                catch (System.Exception)
                {
                    this.disconnect();
                    break;
                }

                if (response.MessageType == WebSocketMessageType.Close)
                {
                    await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close response received",
                        CancellationToken.None);
                    this.SimpleDispatch(SOCKET_EVENT.CLOSE);
                }
                else
                {
                    byteBuffer.WriteBytes(result.Array, 0, response.Count);
                    if (!response.EndOfMessage)
                    {
                        Console.WriteLine("response.EndOfMessage false");
                        continue;
                    }
                    byteBuffer.Position = 0;
                    var code = byteBuffer.ReadUShort(true);
                    try
                    {
                        var value = byteBuffer.ReadObject();
                        if (false == isSync)
                        {
                            Facade.SOCKET.SimpleDispatch(code, value);
                        }
                        else
                        {
                            synchronizationContext.Post(state =>
                            {
                                Facade.SOCKET.SimpleDispatch(code, value);
                            }, null);
                        }



                        Debug.LogFormat("[↓↓↓↓Socket↓↓↓↓] {0} {1}", code, value);
                    }
                    catch (Exception e)
                    {
                        Debug.LogFormat("parse message code={0} byteBuffer={1} ERROR", code, byteBuffer.Position);
                        Debug.LogError(e.Message + "\n" + e.StackTrace);
                    }
                    byteBuffer = new ByteArray();
                }
            }

            // }
            // catch (System.Exception)
            // {
            //     // lock (this)
            //     // {
            //     this.disconnect();
            //     // }
            //     // mainThreadSynContext.Post(new SendOrPostCallback(disconnect), null);
            // }

        }


        /**
         * value => ASObject List<object>
         *          ASObject  {"account":"whh"};
                    var value = new ASObject();
                    value.Add("account", "whh");
                    
                    List<object> [1,2,"3"]
                    var list = new List<object>() {1, 2,"3"};
         */
        public void SimpleSend(ushort code, object value = null)
        {
            var ba = new ByteArray();
            ba.WriteUShort(code, true);
            if (null != value)
            {
                ba.WriteObject(value);
            }

            // var bytes = AmfHelper.write_object(value);
            _webSocket.SendAsync(new ArraySegment<byte>(ba.ToArray()), WebSocketMessageType.Binary, true,
                _cancellation);
        }
    }
}